home *** CD-ROM | disk | FTP | other *** search
/ QuickTime - The Beta Release / QuickTime - The Beta Release.iso / Programming Stuff / Sample Code / Example Compressor / examplecodec.c < prev    next >
Text File  |  1991-09-05  |  31KB  |  1,120 lines

  1. /*
  2.  
  3.     This is an example of am image compression codec that handles both
  4.     compression and decompression of images as passed to it by the 
  5.     Image Compression manager. It is built as a Component Manager Component.
  6.  
  7.  
  8.     The compression scheme here is 411 YUV. The image is stored as separate 
  9.     luminance and chrominance channels. For each 2x2 block of pixels in the
  10.     source image we store 4 luminance (Y) components, 1 Y-Red component (U) and
  11.     1 Y-Blue (V) component. Each Y-component is stored as 6-bits,  resulting in a 
  12.     savings of 2.4:1 over a 24-bit/pixel image (6*4 + 2*8)/4 = 10 bits/pixel.
  13.  
  14. */
  15.  
  16. #include <Memory.h>
  17. #include <Resources.h>
  18. #include <QuickDraw.h>    
  19. #include <QDOffscreen.h>
  20. #include <OSUtils.h>
  21. #include <SysEqu.h>
  22. #include <Errors.h>
  23. #include <FixMath.h>
  24.  
  25. #include "Image Codec.h"
  26.  
  27.  
  28. #define    TYPE_NAME    "\pExample"            /* The name of this type of codec (not this instance) */
  29.  
  30. #define    EXAMPLE_CODEC_REV        1
  31. #define    EXAMPLE_CODEC_VERSION    1
  32.  
  33.  
  34.  
  35. /* some useful macros */
  36.  
  37. #define    R_W    0x4ccd
  38. #define    G_W    0x970a
  39. #define    B_W    0x1c29
  40. #define    PIN(_n)        ((_n) < 0 ? 0 : (_n) > 255 ? 255 : (_n))
  41.  
  42.  
  43.  
  44.  
  45. typedef struct    {                    /* This is the structure we use to store our global data. */
  46.     long    *rwTable;
  47.     long    *gwTable;
  48.     long    *bwTable;
  49.     char    *giwTable;
  50.     CodecInfo    **info;
  51. } SharedGlobals;
  52.  
  53. typedef struct    {                    /* This is the structure we use to store our global data. */
  54.     SharedGlobals    *sharedGlob;
  55. } Globals;
  56.  
  57.  
  58. pascal ComponentResult
  59. OpenCodec(ComponentInstance self);
  60.  
  61. pascal ComponentResult
  62. CloseCodec(Handle storage,ComponentInstance self);
  63.  
  64. pascal ComponentResult
  65. CanDoSelector(short selector);
  66.  
  67. pascal ComponentResult 
  68. GetVersion();
  69.  
  70. pascal void
  71. CompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg);
  72.  
  73. pascal void
  74. DecompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg);
  75.  
  76.  
  77.  
  78.  
  79. /************************************************************************************ 
  80.  *    This is the main dispatcher for our codec. All calls from the codec manager
  81.  *    will come through here, with a unique selector and corresponding parameter block.
  82.  *
  83.  *    This routine must be first in the code segment of the codec thing.
  84.  */
  85.  
  86. pascal long
  87. EXAMPLECODEC(ComponentParameters *thingo,char **storage)
  88. {
  89.     /*    If the selector is less than zero, it's a Component manager selector.    */
  90.     
  91.     if ( thingo->what < 0  ) { 
  92.         switch ( thingo->what ) {
  93.             case kComponentOpenSelect:
  94.                 return CallComponentFunction(thingo, (ComponentFunction) OpenCodec );
  95.     
  96.             case    kComponentCloseSelect:
  97.                 return CallComponentFunctionWithStorage(storage,thingo, (ComponentFunction) CloseCodec );
  98.                 
  99.             case    kComponentCanDoSelect:
  100.                 return CallComponentFunction(thingo, (ComponentFunction) CanDoSelector);
  101.     
  102.             case kComponentVersionSelect : 
  103.                 return CallComponentFunction(thingo, (ComponentFunction) GetVersion);
  104.     
  105.             default :
  106.                 return (paramErr);
  107.         }
  108.     }
  109.     
  110.     /*
  111.      *    Here we dispatch the rest of our calls. We use the magic thing manager routine which
  112.      *    calls our subroutines with the proper parameters. The prototypes are in Image Codec.h.
  113.      */
  114.     
  115.     switch ( thingo->what ) {
  116.         case codecPreCompress:    
  117.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDPreCompress);
  118.             
  119.         case codecBandCompress:
  120.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDBandCompress);
  121.             
  122.         case codecPreDecompress:
  123.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDPreDecompress);
  124.     
  125.         case codecBandDecompress:
  126.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDBandDecompress);
  127.     
  128.         case codecCDSequenceBusy:
  129.             return 0;                    /* our codec is never asynchronously busy */
  130.     
  131.         case codecGetCodecInfo:
  132.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDGetCodecInfo);
  133.             
  134.         case codecGetCompressedImageSize:
  135.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDGetCompressedImageSize);
  136.     
  137.         case codecGetMaxCompressionSize:
  138.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDGetMaxCompressionSize);
  139.     
  140.         case codecGetCompressionTime:
  141.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDGetCompressionTime);
  142.     
  143.         case codecGetSimilarity:
  144.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDGetSimilarity);
  145.     
  146.         case codecTrimImage:
  147.             return CallComponentFunctionWithStorage(storage,thingo,(ComponentFunction)CDTrimImage);
  148.         
  149.         default:
  150.             return(paramErr);
  151.     }    
  152. }
  153.  
  154.  
  155. /************************************************************************************ 
  156.  *    This gets called when the thing instance is opened. We allocate our storage at this
  157.  *    point. If we have shared globals, we check if they exist, and put a pointer to them 
  158.  *    in our instance globals so that other calls can get to them.
  159.  */
  160.  
  161. pascal ComponentResult
  162. OpenCodec(ComponentInstance self)
  163. {
  164.     SharedGlobals    *sGlob;
  165.     long            i;
  166.     Globals             **glob;
  167.     short            resFile;
  168.     THz                saveZone;
  169.     Handle            h;
  170.     Boolean            inAppHeap;
  171.     OSErr            result = noErr;
  172.         
  173.     /* 
  174.         First we allocate storage. This should be a handle to any
  175.         kind of data used by the thing instance.
  176.     
  177.         This is put in the system heap by default, or in the app
  178.         heap if the codec is opened in the app heap.
  179.     
  180.         If you have data which can be shared among several instances (such
  181.         as look-up tables ), then you should
  182.         use the ComponentManager call "SetComponentRefCon" for this.
  183.      */
  184.      
  185.     inAppHeap = ( GetComponentInstanceA5(self) != 0 );
  186.     saveZone = GetZone();
  187.     if ( !inAppHeap )
  188.         SetZone(SystemZone());
  189.          
  190.     if ( (glob = (Globals **)NewHandleClear(sizeof(Globals))) == nil )  {
  191.         result = MemError();
  192.         goto obail;
  193.     }
  194.     
  195.     SetComponentInstanceStorage(self,(Handle)glob);
  196.  
  197.     /*
  198.      *    Allocate and initialize tables. These sare shared across all instances
  199.      *    of this codecs, and are kept in the refcon.
  200.      */
  201.  
  202.     if ( (sGlob=(SharedGlobals*)GetComponentRefcon((Component)self)) == nil  ) {
  203.         if ( (sGlob = (SharedGlobals*)NewPtrSys(sizeof(SharedGlobals))) != nil ) { 
  204.             if ((sGlob->rwTable = (long *)NewPtrSys(256*sizeof(long))) == nil ) {
  205.                 DisposPtr((Ptr)sGlob);
  206.                 result = MemError();
  207.                 goto obail;
  208.             }
  209.             if ((sGlob->gwTable = (long *)NewPtrSys(256*sizeof(long))) == nil ) {
  210.                 DisposPtr((Ptr)sGlob->rwTable);
  211.                 DisposPtr((Ptr)sGlob);
  212.                 result = MemError();
  213.                 goto obail;
  214.             }
  215.             if ( (sGlob->bwTable = (long *)NewPtrSys(256*sizeof(long))) == nil ) {
  216.                 DisposPtr((Ptr)sGlob->rwTable);
  217.                 DisposPtr((Ptr)sGlob->gwTable);
  218.                 DisposPtr((Ptr)sGlob);
  219.                 result = MemError();
  220.                 goto obail;
  221.             }
  222.             if ( (sGlob->giwTable = NewPtrSys(256)) == nil ) {
  223.                 DisposPtr((Ptr)sGlob->rwTable);
  224.                 DisposPtr((Ptr)sGlob->gwTable);
  225.                 DisposPtr((Ptr)sGlob->bwTable);
  226.                 DisposPtr((Ptr)sGlob);
  227.                 result = MemError();
  228.                 goto obail;
  229.             }
  230.             for ( i=0; i < 256; i++ )
  231.                 sGlob->rwTable[i] = i * R_W;
  232.             for ( i=0; i < 256; i++ )
  233.                 sGlob->gwTable[i] = i * G_W;
  234.             for ( i=0; i < 256; i++ )
  235.                 sGlob->bwTable[i] = i * B_W;
  236.             for ( i=0; i < 256; i++ )
  237.                 sGlob->giwTable[i] = PIN((i<<16) / G_W);
  238.             SetComponentRefcon((Component)self,(long)sGlob);
  239.             resFile = OpenComponentResFile((Component)self);
  240.             SetZone(SystemZone());
  241.             h = Get1Resource(codecInfoResourceType,128);
  242.             if ( h == nil ) {
  243.                 DisposHandle((Handle)glob);
  244.                 result = ResError();
  245.                 goto obail;
  246.             }
  247.             LoadResource(h);
  248.             if ( ResError() ) {
  249.                 DisposHandle((Handle)glob);
  250.                 result = ResError();
  251.                 goto obail;
  252.             }
  253.             DetachResource(h);
  254.             HNoPurge(h);
  255.             CloseComponentResFile(resFile);
  256.             SetZone(saveZone);
  257.             if( h == 0 )  {
  258.                 result = MemError();
  259.                 goto obail;
  260.             }
  261.             sGlob->info = (CodecInfo **)h;
  262.         }
  263.     }
  264.     (*glob)->sharedGlob = sGlob;
  265. obail:
  266.     return(result);
  267. }
  268.  
  269.  
  270. /************************************************************************************ 
  271.  *    This gets called when the thing instance is closed. We need to get rid of any 
  272.  *    instance storage here. 
  273.  */
  274.  
  275. pascal ComponentResult
  276. CloseCodec(Handle storage,ComponentInstance self)
  277. {
  278.     SharedGlobals    *sGlob;
  279.     long            i;
  280.     Globals            **glob = (Globals **)storage;
  281.         
  282.     /*    If we are closing our last instance, then we can chuck our shared globals. */
  283.     
  284.     if ( CountComponentInstances((Component)self) == 1) {
  285.         if ( (sGlob=(SharedGlobals*)GetComponentRefcon((Component)self)) != nil  ) {
  286.             if ( sGlob->rwTable ) 
  287.                 DisposPtr((Ptr)sGlob->rwTable);
  288.             if ( sGlob->gwTable ) 
  289.                 DisposPtr((Ptr)sGlob->gwTable);
  290.             if ( sGlob->bwTable ) 
  291.                 DisposPtr((Ptr)sGlob->bwTable);
  292.             if ( sGlob->giwTable ) 
  293.                 DisposPtr((Ptr)sGlob->giwTable);
  294.             DisposPtr((Ptr)sGlob);
  295.             SetComponentRefcon((Component)self,0);
  296.         }
  297.     }
  298.     
  299.     /*    Dispose instance storage. */
  300.     
  301.     if ( glob )    
  302.         DisposHandle((Handle)storage);
  303.     return(noErr);
  304. }
  305.  
  306.  
  307.  
  308. /************************************************************************************ 
  309.  *     Return true if we can handle the selector, otherwise false.
  310.  */
  311.  
  312. pascal ComponentResult
  313. CanDoSelector(short selector)
  314. {    
  315.     switch(selector) {
  316.         case codecPreCompress:
  317.         case codecBandCompress:
  318.         case codecPreDecompress:
  319.         case codecBandDecompress:
  320.         case codecCDSequenceBusy:
  321.         case codecGetCodecInfo:
  322.         case codecGetCompressedImageSize:
  323.         case codecGetMaxCompressionSize:
  324.         case codecGetCompressionTime:
  325.         case codecGetSimilarity:
  326.         case codecTrimImage:
  327.             return(true);
  328.         default:
  329.             return (false);
  330.     }
  331. }
  332.  
  333.  
  334. /************************************************************************************ 
  335.  *    Return the version of this component ( defines interface ) and revision level
  336.  *    of the code.
  337.  */
  338.  
  339. pascal ComponentResult 
  340. GetVersion()
  341. {
  342.     return ((EXAMPLE_CODEC_VERSION<<16) | EXAMPLE_CODEC_REV);        /* interface version in hi word, code rev in lo word  */
  343. }
  344.  
  345.  
  346. /************************************************************************************ 
  347.  *    CDPreCompress gets called before an image is compressed, or whenever the source pixmap
  348.  *    pixel size changes when compressing a sequence. We return information about
  349.  *    how we can compress the image to the codec manager, so that it can fit the source data
  350.  *    to our requirements. The ImageDescriptor is already filled in, so we can use it for 
  351.  *    reference (or even add to it ). PixelSize is the pixel depth of the source pixmap, we
  352.  *    use this as a reference for deciding what we can do. The other parameters return information
  353.  *    to the CodecManager about what we can do. We can also do setup here if we want to.
  354.  */
  355.  
  356. pascal long
  357. CDPreCompress(Handle storage,register CodecCompressParams *p)
  358. {
  359.     CodecCapabilities *capabilities = p->capabilities;
  360.  
  361.     /*
  362.      *    First we return which depth input pixels we can deal with - based on what the
  363.      *    app has available - we can only work with 32 bit input pixels.
  364.      */
  365.        
  366.     switch ( (*p->imageDescription)->depth )  {
  367.         case 16:
  368.             capabilities->wantedPixelSize = 32;
  369.             break;
  370.         default:
  371.             return(paramErr);
  372.             break;
  373.     }
  374.  
  375.     /* if the buffer is banded -  return the smallest one we can deal with */
  376.     
  377.     capabilities->bandMin = 2;
  378.  
  379.     /* if the buffer is banded, return the increment it be should grown */
  380.  
  381.     capabilities->bandInc = 2;
  382.     
  383.     
  384.     /*
  385.      *    If a codec needs the dimensions of the source pixmap to be of certain multiples
  386.      *    it can ask for the image to be extended out (via pixel replication) vertically
  387.      *    and/or horizontally.
  388.      *
  389.      *    In our case, we're dealing with 2 by 2 blocks and therefore we want the image
  390.      *    height and width to both be multiples of 2.  If either dimension is odd, we
  391.      *    ask it have it extended by one pixel.
  392.      */
  393.  
  394.     capabilities->extendWidth = (*p->imageDescription)->width & 1;
  395.     capabilities->extendHeight = (*p->imageDescription)->height & 1;
  396.     
  397.     return(noErr);
  398. }
  399.  
  400.  
  401. /************************************************************************************ 
  402.  *    CDBandCompress gets called when the codec manager wants us to compress an image, or a horizontal 
  403.  *    band of an image. The pixel data at sBaseAddr is guaranteed to conform to the criteria we 
  404.  *    demanded in BeginCompress.
  405.  */
  406.  
  407. pascal long
  408. CDBandCompress(Handle storage,register CodecCompressParams *p)
  409. {
  410.     short                width,height;
  411.     Ptr                    cDataPtr,dataStart;
  412.     long                totalBytes = 0;
  413.     short                depth;
  414.     Rect                sRect;
  415.     long                offsetH,offsetV;
  416.     Globals                **glob  = (Globals **)storage;
  417.     register char         *baseAddr;
  418.     short                numLines = p->stopLine - p->startLine;
  419.     short                rowBytes,stripBytes;
  420.     char                mmuMode = 1;
  421.     register short        y;
  422.     ImageDescription    **desc = p->imageDescription;
  423.     OSErr                result = noErr;
  424.     
  425.     /*    If there is a progress proc, give it an open call at the start of this band. */
  426.  
  427.     if (p->progressProcRecord.progressProc)
  428.         p->progressProcRecord.progressProc(codecProgressOpen,0,
  429.             p->progressProcRecord.progressRefCon);
  430.  
  431.     width = (*desc)->width;
  432.     height = (*desc)->height;
  433.     depth = (*desc)->depth;
  434.     dataStart = cDataPtr = StripAddress(p->data);
  435.  
  436.     /* figure out offset to first pixel in baseAddr from the pixelsize and bounds */
  437.  
  438.     rowBytes = p->srcPixMap.rowBytes & 0x3fff;
  439.     sRect =  p->srcPixMap.bounds;
  440.     offsetH = sRect.left<<2;
  441.     offsetV = sRect.top * rowBytes;
  442.     baseAddr = p->srcPixMap.baseAddr += offsetH + offsetV;
  443.     stripBytes = ((width+1)>>1) * 5;
  444.     
  445.     cDataPtr += (p->startLine>>1) * stripBytes;
  446.     if ( p->flushProcRecord.flushProc ) {    
  447.         if ( p->bufferSize < stripBytes ) {
  448.             result = codecSpoolErr;
  449.             goto bail;
  450.         }
  451.     }
  452.  
  453.     for ( y=0; y < ((numLines+1)>>1); y++) {
  454.         SwapMMUMode(&mmuMode);
  455.         CompressStrip(cDataPtr,baseAddr,rowBytes,width,(*glob)->sharedGlob);
  456.         SwapMMUMode(&mmuMode);
  457.         cDataPtr += stripBytes;
  458.         baseAddr += rowBytes<<1;
  459.         if ( p->flushProcRecord.flushProc ) { 
  460.             if ( (result=p->flushProcRecord.flushProc(dataStart,stripBytes,
  461.                     p->flushProcRecord.flushRefCon)) != noErr) {
  462.                 goto bail;
  463.             }
  464.             cDataPtr = dataStart;
  465.         }
  466.         if (p->progressProcRecord.progressProc) {
  467.             if ( (result=p->progressProcRecord.progressProc(codecProgressUpdatePercent,
  468.                 FixDiv(y,(numLines+1)>>1),p->progressProcRecord.progressRefCon)) != noErr )
  469.                     goto bail;
  470.         }
  471.     }
  472.  
  473.     
  474.     if ( p->conditionFlags & codecConditionLastBand ) {
  475.         (*p->imageDescription)->dataSize = ((width+1)>>1) * 5 * ((height+1)>>1);    /* return the actual size of the compressed data */
  476.         p->similarity = 0;                            /* we don't do frame differencing */
  477.     }
  478.     
  479. bail:
  480.     /*    If there is a progress proc, give it a close call at the end of this band. */
  481.  
  482.     if (p->progressProcRecord.progressProc)
  483.         p->progressProcRecord.progressProc(codecProgressClose,0,
  484.             p->progressProcRecord.progressRefCon);
  485.         
  486.     return(result);
  487. }
  488.  
  489.  
  490.  
  491. /************************************************************************************ 
  492.  *    CDPreDecompress gets called before an image is decompressed. We return information about
  493.  *    how we can decompress the image to the codec manager, so that it can fit the destination data
  494.  *    to our requirements. 
  495.  */
  496.  
  497. pascal long
  498. CDPreDecompress(Handle storage,register CodecDecompressParams *p)
  499. {
  500.     register CodecCapabilities    *capabilities = p->capabilities;
  501.     Rect    dRect = p->srcRect;
  502.     
  503.     /*    Check if the matrix is okay for us. We don't do anything fancy. */
  504.     
  505.     if ( !TransformRect(p->matrix,&dRect,nil) )
  506.         return(paramErr);
  507.  
  508.  
  509.     /*    Decide which depth compressed data we can deal with. */
  510.     
  511.     switch ( (*p->imageDescription)->depth )  {
  512.         case 16:
  513.             break;
  514.         default:
  515.             return(paramErr);
  516.             break;
  517.     }
  518.  
  519.     /*    We can deal only 32 bit pixels. */
  520.  
  521.     capabilities->wantedPixelSize = 32;    
  522.     
  523.     /*    The smallest possible band we can do is 2 scan lines. */
  524.     
  525.     capabilities->bandMin = 2;
  526.  
  527.     /*    We can deal with 2 scan line high bands. */
  528.  
  529.     capabilities->bandInc = 2;
  530.     
  531.     /*    If we needed our pixels to be aligned on some integer multiple we would set these to
  532.      *    the number of pixels we need the dest extended by.
  533.      */
  534.  
  535.     capabilities->extendWidth = p->srcRect.right & 1;
  536.     capabilities->extendHeight = p->srcRect.bottom & 1;
  537.     
  538.     return(noErr);
  539. }
  540.  
  541.  
  542. /************************************************************************************ 
  543.  *    CDBandDecompress gets called when the codec manager wants us to decompress an image or a horizontal 
  544.  *    band of an image. The pixel data at baseAddr is guaranteed to conform to the criteria we 
  545.  *    demanded in BeginDecompress. If maskIn is true, then the mask data at mBaseAddr is valid, and
  546.  *    we need to clear bits in it that correspond to any pixels in the destination we do not want to 
  547.  *    change. ( We always write all pixels, so we dont care. This mode is important only for those
  548.  *    codecs that have frame differencing and don't always write all the pixels. )
  549.  */
  550.  
  551. pascal long
  552. CDBandDecompress(Handle storage,register CodecDecompressParams *p)
  553. {
  554.     Rect                dRect;
  555.     long                offsetH,offsetV;
  556.     Globals                **glob  = (Globals **)storage;
  557.     short                numLines = p->stopLine - p->startLine;
  558.     short                rowBytes,stripBytes;
  559.     short                width;
  560.     register short        y;
  561.     register char        *baseAddr;
  562.     char                *cDataPtr;
  563.     char                mmuMode = 1;
  564.     OSErr                result = noErr;
  565.     
  566.     /*    Calculate the real base address based on the bounds rect. If its not 
  567.         a linear transformation, we dont do it. */
  568.  
  569.     dRect = p->srcRect;
  570.     if ( !TransformRect(p->matrix,&dRect,nil) )
  571.         return(paramErr);
  572.  
  573.     /*    If there is a progress proc, give it an open call at the start of this band. */
  574.  
  575.     if (p->progressProcRecord.progressProc)
  576.         p->progressProcRecord.progressProc(codecProgressOpen,0,
  577.             p->progressProcRecord.progressRefCon);
  578.     
  579.     width = (*p->imageDescription)->width;
  580.     rowBytes = p->dstPixMap.rowBytes;    
  581.     offsetH = (dRect.left - p->dstPixMap.bounds.left) * sizeof(long);
  582.     offsetV = (dRect.top - p->dstPixMap.bounds.top) * rowBytes;
  583.     baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
  584.  
  585.     stripBytes = ((width+1)>>1) * 5;
  586.  
  587.     cDataPtr = StripAddress(p->data);
  588.  
  589.     /* 
  590.      *    If we are skipping some data, we just skip it here. We can tell because
  591.      *    firstBandInFrame says this is the first band for a new frame, and
  592.      *    if startLine is not zero, then that many lines were clipped out.
  593.      */
  594.  
  595.     if ( (p->conditionFlags & codecConditionFirstBand) && p->startLine != 0 ) {
  596.         if ( p->dataProcRecord.dataProc ) {
  597.             for ( y=0; y  < p->startLine>>1; y++ )  {
  598.                 if ( (result=p->dataProcRecord.dataProc(&cDataPtr,stripBytes,
  599.                         p->dataProcRecord.dataRefCon)) != noErr ) {
  600.                     goto bail;
  601.                 }
  602.                 cDataPtr += stripBytes;
  603.             }
  604.         } else
  605.             cDataPtr += (p->startLine>>1) * stripBytes;
  606.     }
  607.     
  608.     /*
  609.      *    If theres a dataproc spooling the data to us, then we have to do the data
  610.      *    in whatever size chunks they want to give us.
  611.      */
  612.     
  613.     for (y=0; y < (numLines+1)>>1; y++) {
  614.         if (p->dataProcRecord.dataProc) {
  615.             if ( (result=p->dataProcRecord.dataProc(&cDataPtr,stripBytes,
  616.                     p->dataProcRecord.dataRefCon)) != noErr ) {
  617.                 goto bail;
  618.             }
  619.         }
  620.         SwapMMUMode(&mmuMode);
  621.         DecompressStrip(cDataPtr,baseAddr,rowBytes,width,(*glob)->sharedGlob);
  622.         SwapMMUMode(&mmuMode);
  623.         baseAddr += rowBytes<<1;
  624.         cDataPtr += stripBytes;
  625.         if (p->progressProcRecord.progressProc) {
  626.             if ( (result=p->progressProcRecord.progressProc(codecProgressUpdatePercent,
  627.                 FixDiv(y, (numLines+1)>>1),p->progressProcRecord.progressRefCon)) != noErr ) {
  628.                  goto bail;
  629.             }
  630.         }
  631.     }
  632.  
  633.     p->data = cDataPtr;
  634.     
  635.     if ( p->conditionFlags & codecConditionLastBand ) {
  636.         /* Tie up any loose ends on the last band of the frame. */
  637.     }
  638.  
  639. bail:
  640.     /*    If there is a progress proc, give it a close call at the end of this band. */
  641.  
  642.     if (p->progressProcRecord.progressProc)
  643.         p->progressProcRecord.progressProc(codecProgressClose,0,
  644.             p->progressProcRecord.progressRefCon);
  645.  
  646.  
  647.     return(result);
  648. }
  649.  
  650.  
  651. /************************************************************************************ 
  652.  *    CDGetCodecInfo allows us to return information about ourselves to the codec manager.
  653.  *    
  654.  *    There will be a tool for determining appropriate values for the accuracy, speed
  655.  *    and level information. For now we estimate with scientific wild guessing.
  656.  */
  657.  
  658. pascal ComponentResult
  659. CDGetCodecInfo(Handle storage,CodecInfo *info)
  660. {
  661.     Globals **glob = (Globals **)storage;
  662.  
  663.     if ( info == nil ) 
  664.         return(paramErr);
  665.     BlockMove((Ptr)*((*glob)->sharedGlob)->info,(Ptr)info,sizeof(CodecInfo));
  666.     return(noErr);
  667. }
  668.  
  669.  
  670. /************************************************************************************ 
  671.  *    When CDGetSimilarity is called, we return the percent of the compressed image A that
  672.  *    is different from compressed image B. This can be used by applications that use sequence
  673.  *    dynamics as an indicator for editing image sequences.
  674.  *    
  675.  *    If the codec cannot do a direct similarity comparison, the ICM decompresses image A and
  676.  *    do a comparison with image B.  This call is provided so that a codec can save the time
  677.  *    of the intermediate decompress if it can do the comparison directly.
  678.  */
  679.  
  680. pascal ComponentResult
  681. CDGetSimilarity(Handle storage,PixMap **src,Rect *srcRect,ImageDescriptionHandle desc,
  682.                 char *data,Fixed *dif)
  683. {
  684. #pragma    unused(storage,src,srcRect,desc,data,dif,refcon)
  685.  
  686.     /*    This call is not implemented yet. */
  687.  
  688.     return(codecUnimpErr);
  689. }
  690.  
  691.  
  692. /************************************************************************************ 
  693.  *    When CDGetCompressedImageSize is called, we return the size in bytes of the given compressed
  694.  *    data ( for one image frame).
  695.  */
  696.  
  697. pascal ComponentResult
  698. CDGetCompressedImageSize(Handle storage,ImageDescriptionHandle desc,Ptr data,long dataSize,
  699.     DataProcRecordPtr dataProc,long *size)
  700. {
  701. #pragma    unused(storage,data,dataSize,dataProc)
  702.  
  703.     short    width =(*desc)->width;
  704.     short    height = (*desc)->height;
  705.     
  706.     if ( size == nil )
  707.         return(paramErr);
  708.         
  709.     /*
  710.      *    Our data has a size which is deterministic based on the image size. If it were not we
  711.      *    could encode the size in the compressed data, or figure it out by walking the
  712.      *    compressed data.
  713.      */
  714.      
  715.     *size = ((width+1)>>1) * 5 * ((height+1)>>1);
  716.     return(noErr);
  717. }
  718.  
  719.  
  720. /************************************************************************************ 
  721.  *    When CDGetMaxCompressionSize is called, we return the maximum size the compressed data for
  722.  *    the given image would be in bytes.
  723.  */
  724.  
  725. pascal ComponentResult
  726. CDGetMaxCompressionSize(Handle storage,PixMap **src,Rect *srcRect,short depth,CodecQ quality,
  727.         long *size)
  728. {
  729. #pragma    unused(storage,src,depth,quality)
  730.     
  731.     short width = srcRect->right - srcRect->left;
  732.     short height = srcRect->bottom - srcRect->top;
  733.  
  734.     /*    we always end up with a fixed size. If we did not, we would return the worst case size */
  735.     
  736.     *size = ((width+1)>>1) * 5 * ((height+1)>>1);    
  737.  
  738.     return(noErr);
  739. }
  740.  
  741.  
  742. /************************************************************************************ 
  743.  *    When CDGetCompressionTime is called, we return the approximate time for compressing
  744.  *    the given image would be in milliseconds. We also return the closest actual quality
  745.  *    we can handle for the requested value.
  746.  */
  747.  
  748. pascal ComponentResult
  749. CDGetCompressionTime(Handle storage,PixMap **src,Rect *srcRect,short depth,CodecQ *spatialQuality,
  750.         CodecQ *temporalQuality,unsigned long *time)
  751. {
  752. #pragma    unused(storage,src,srcRect,depth)
  753.  
  754.     if (time)
  755.         *time = 0;                                    /* we don't know how many */
  756.  
  757.     if (spatialQuality)
  758.         *spatialQuality = codecNormalQuality;        /* we have only one quality level for now */
  759.     
  760.     if (temporalQuality)
  761.         *temporalQuality = 0;                        /* we cannot do temporal compression */
  762.     return(noErr);
  763.  
  764. }
  765.  
  766.  
  767. /************************************************************************************ 
  768.  *    When CDTrimImage is called, we take the given compressed data and return only the portion
  769.  *    which is represented by the trimRect. We can return a little more if we have too, but we
  770.  *    need only return enough so that the image in trimRect is properly displayed. We then
  771.  *    adjust the rectangle to corresond to the same rectangle in the new trimmed data.
  772.  */
  773.  
  774. pascal ComponentResult
  775. CDTrimImage(Handle storage,ImageDescriptionHandle desc,Ptr inData,long inDataSize,
  776.         DataProcRecordPtr dataProc,Ptr outData,long outDataSize,FlushProcRecordPtr flushProc,
  777.         Rect *trimRect,ProgressProcRecordPtr progressProc)
  778. {
  779. #pragma    unused(storage)
  780.  
  781.     Rect    rect = *trimRect;
  782.     char    *dataP,*odP,*startP;
  783.     short    trimOffTop;
  784.     short    trimOffBottom;
  785.     short    trimOffLeft;
  786.     short    trimOffRight;
  787.     short    bytesOffLeft;
  788.     short    newHeight,newWidth;
  789.     long    size;
  790.     short    stripBytes;
  791.     short    newStripBytes;
  792.     short    i,y;
  793.     OSErr    result = noErr;
  794.     
  795.         
  796.                     
  797.     /* we dont handle spooling yet */
  798.     
  799.     
  800.     if ( dataProc->dataProc == nil )
  801.         dataProc = nil;
  802.     if ( flushProc->flushProc == nil )
  803.         flushProc = nil;
  804.     if ( progressProc->progressProc == nil )
  805.         progressProc = nil;
  806.         
  807.     if ( progressProc ) 
  808.         progressProc->progressProc(codecProgressOpen,0,progressProc->progressRefCon);
  809.     dataP = inData;
  810.     newHeight = (*desc)->height;
  811.     newWidth = (*desc)->width;
  812.     stripBytes = ((newWidth+1)>>1) * 5;            /* the number of bytes in a strip (2-scanlines/strip) */
  813.     
  814.     /* figure out how many 2x2 blocks we want to strip from each side of the image */
  815.  
  816.     trimOffTop = rect.top>>1;
  817.     trimOffBottom  = (newHeight - rect.bottom) >> 1;
  818.     trimOffLeft = rect.left>>1;
  819.     trimOffRight  = (newWidth - rect.right) >> 1;
  820.  
  821.     /* point to the start of the first strip we are using */
  822.  
  823.     startP  = dataP + stripBytes * trimOffTop;
  824.  
  825.  
  826.     /* make the trim values pixel based */
  827.     
  828.     trimOffLeft <<= 1;
  829.     trimOffTop <<= 1;
  830.     trimOffBottom <<= 1;
  831.     trimOffRight <<= 1;
  832.     
  833.     /* calculate new height and width */
  834.     
  835.     newHeight -= trimOffTop + trimOffBottom;
  836.     newWidth -=  trimOffLeft + trimOffRight;
  837.     
  838.     /* calc size in bytes of strips of the new width */
  839.     
  840.     newStripBytes = ((newWidth+1)>>1) * 5;        
  841.  
  842.     /* figure number of bytes to toss at the beginning of each strip  */
  843.     
  844.     bytesOffLeft = (trimOffLeft>>1) * 5;
  845.  
  846.     /* figure size of new trimmed image */
  847.     
  848.     size = newStripBytes * (newHeight>>1);
  849.     
  850.     /* make sure it's gonna fit */
  851.     
  852.     if ( size > outDataSize )  {
  853.         result = codecErr;
  854.         goto bail;
  855.     }
  856.         
  857.     /* now go through the strips and copy the needed portion of each to the new data */
  858.  
  859.     if (  dataProc ) {
  860.         short rightBytes = stripBytes - newStripBytes - bytesOffLeft;
  861.         for ( y=0; y < trimOffTop; y++ ) {
  862.             if ( (result=dataProc->dataProc(&inData,stripBytes,dataProc->dataRefCon)) != noErr )
  863.                 goto bail;
  864.             inData += stripBytes;
  865.             if (progressProc ) {
  866.                 if ( (result=progressProc->progressProc(codecProgressUpdatePercent,
  867.                     FixDiv(y, (*desc)->height),progressProc->progressRefCon)) != noErr) 
  868.                     goto bail;
  869.             }
  870.         }
  871.         for ( y=0; y < newHeight; y+= 2) {
  872.             if ( bytesOffLeft ) {
  873.                 if ( (result=dataProc->dataProc(&inData,bytesOffLeft,dataProc->dataRefCon)) != noErr )
  874.                     goto bail;
  875.                 inData += bytesOffLeft;
  876.             }
  877.             if ( (result=dataProc->dataProc(&inData,newStripBytes,dataProc->dataRefCon)) != noErr )
  878.                 goto bail;
  879.             if (  flushProc ) {
  880.                 if ( (result=flushProc->flushProc(inData,newStripBytes,flushProc->flushRefCon)) != noErr )
  881.                     goto bail;
  882.             }
  883.             else {
  884.                 BlockMove(inData,outData,newStripBytes);
  885.                 outData += newStripBytes;
  886.             }
  887.             inData += newStripBytes;
  888.             if ( rightBytes ) {
  889.                 if ( (result=dataProc->dataProc(&inData,rightBytes,dataProc->dataRefCon)) != noErr )
  890.                     goto bail;
  891.                 inData += rightBytes;
  892.             }
  893.             if (progressProc) {
  894.                 if ( (result=progressProc->progressProc(codecProgressUpdatePercent,
  895.                     FixDiv((trimOffTop + y),(*desc)->height),progressProc->progressRefCon)) != noErr )
  896.                     goto bail;
  897.             }
  898.         }
  899.     }
  900.     else {
  901.         inData += stripBytes * trimOffTop;
  902.         for ( y=0; y < newHeight; y += 2, inData += stripBytes) {
  903.             if (  flushProc ) {
  904.                 if ( (result=flushProc->flushProc(inData + bytesOffLeft,newStripBytes,flushProc->flushRefCon)) != noErr )
  905.                     goto bail;
  906.             }
  907.             else {
  908.                 BlockMove(inData + bytesOffLeft,outData,newStripBytes);
  909.                 outData += newStripBytes;
  910.             }
  911.             if (progressProc ) {
  912.                 if ( (result=progressProc->progressProc(codecProgressUpdatePercent,
  913.                     FixDiv((trimOffTop + y),(*desc)->height),progressProc->progressRefCon)) != noErr )
  914.                     goto bail;
  915.             }
  916.         }
  917.     }
  918.  
  919.     /* adjust the rectangle to reflect our changes */
  920.     
  921.     trimRect->top -= trimOffTop;
  922.     trimRect->bottom -= trimOffTop;
  923.     trimRect->left -= trimOffLeft;
  924.     trimRect->right -= trimOffLeft;
  925.  
  926.     /* return the new width and height in the image description and the size */
  927.     
  928.     (*desc)->height = newHeight;
  929.     (*desc)->width = newWidth;
  930.     (*desc)->dataSize = size;
  931. bail:
  932.     if ( progressProc ) 
  933.         progressProc->progressProc(codecProgressClose,0,progressProc->progressRefCon);
  934.  
  935.     return(result);
  936.  
  937.  
  938. }
  939.  
  940.     
  941. #ifndef    HAS_ASM        /* we could do this part in assembly for speed if we desired */
  942.  
  943.  
  944. #define    READPIXEL(n)                \
  945.     l = *lp++;                        \
  946.     r = (l>>16);                    \
  947.     g = (l>>8);                        \
  948.     b = l;                            \
  949.     yt = (R_W*r + G_W*g + B_W*b);    \
  950.     if ( yt > ((256L<<16)-1) ) yt = ((256L<<16)-1); \
  951.     ys[n] = yt>>16;                    \
  952.     y += yt;                        \
  953.     u += r;                            \
  954.     v += b;                        
  955.  
  956. #define    READPIXEL_T(n)                \
  957.     l = *lp++;                        \
  958.     r = (l>>16);                    \
  959.     g = (l>>8);                        \
  960.     b = l;                            \
  961.     yt = (sg->rwTable[r] + sg->gwTable[g] + sg->bwTable[b]);    \
  962.     if ( yt > ((256L<<16)-1) ) yt = ((256L<<16)-1); \
  963.     ys[n] = yt>>16;                    \
  964.     y += yt;                        \
  965.     u += r;                            \
  966.     v += b;                        
  967.  
  968.  
  969. pascal void
  970. CompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg)
  971. {
  972.  
  973.     register long    l,*lp = (long *)baseAddr;
  974.     register unsigned char     r,g,b;
  975.     unsigned char    ys[4];
  976.     register long    y,yt;
  977.     short    u,v;
  978.     short    rowLongs = (rowBytes>>2);
  979.     
  980.     
  981.     len++;
  982.     len>>=1;
  983.     if ( sg ) {
  984.         while ( len-- > 0) {
  985.             y = u = v = 0;
  986.             READPIXEL_T(0);
  987.             READPIXEL_T(1);
  988.             lp += rowLongs-2;
  989.             READPIXEL_T(2);
  990.             READPIXEL_T(3);
  991.             lp -= rowLongs;
  992.         
  993.             y >>= 16;
  994.             u = (u - y)>>4;
  995.             v = (v - y)>>4;
  996.             
  997.             l =  (long)(0xfc & (ys[0])) << 24;
  998.             l |= (long)(0xfc & (ys[1])) << 18;
  999.             l |= (long)(0xfc & (ys[2])) << 12;
  1000.             l |= (long)(0xfc & (ys[3])) <<  6;
  1001.             l |= u & 0xff;
  1002.             *(long *)data = l;
  1003.             data += sizeof(long);
  1004.             *data++ = v;
  1005.         }
  1006.     } else {
  1007.         while ( len-- > 0) {
  1008.             y = u = v = 0;
  1009.             READPIXEL(0);
  1010.             READPIXEL(1);
  1011.             lp += rowLongs-2;
  1012.             READPIXEL(2);
  1013.             READPIXEL(3);
  1014.             lp -= rowLongs;
  1015.         
  1016.             y >>= 16;
  1017.             u = (u - y)>>4;
  1018.             v = (v - y)>>4;
  1019.             
  1020.             l =  (long)(0xfc & (ys[0])) << 24;
  1021.             l |= (long)(0xfc & (ys[1])) << 18;
  1022.             l |= (long)(0xfc & (ys[2])) << 12;
  1023.             l |= (long)(0xfc & (ys[3])) <<  6;
  1024.             l |= u & 0xff;
  1025.             *(long *)data = l;
  1026.             data += sizeof(long);
  1027.             *data++ = v;
  1028.         }
  1029.     }
  1030. }
  1031.  
  1032.  
  1033.  
  1034.  
  1035. #define    WRITEPIXEL                \
  1036.     r = PIN(u+y);                \
  1037.     b = PIN(v+y);                \
  1038.     y <<= 16;                    \
  1039.     y -= r * R_W;                \
  1040.     y -= b * B_W;                \
  1041.     g = PIN(y / G_W);            \
  1042.     *lp++ = (long) ( (long) r <<16) | ( (long) g <<8) | b;    
  1043.  
  1044. #define    WRITEPIXEL_T            \
  1045.     r = PIN(u+y);                \
  1046.     b = PIN(v+y);                \
  1047.     y <<= 16;                    \
  1048.     y -= sg->rwTable[r];        \
  1049.     y -= sg->bwTable[b];        \
  1050.     g = sg->giwTable[PIN(y>>16)];    \
  1051.     *lp++ = (long) ( (long) r <<16) | ( (long) g <<8) | b;    
  1052.     
  1053. pascal void
  1054. DecompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg)
  1055. {
  1056.     register long    y;
  1057.     register unsigned char     r,g,b;
  1058.     register long    l,*lp;
  1059.     long     u,v;
  1060.     unsigned char    ys[4];
  1061.     short    rowLongs = (rowBytes>>2);
  1062.     short    blen = len;
  1063.  
  1064.     lp = (long *)baseAddr;
  1065.     blen++;
  1066.     blen >>= 1;
  1067.     if ( sg ) {
  1068.         while ( blen-- > 0 ) {
  1069.             l = *(long *)data;
  1070.             data += sizeof(long);
  1071.             ys[0] = (0xfc & (l>>24));
  1072.             ys[1] = (0xfc & (l>>18));
  1073.             ys[2] = (0xfc & (l>>12));
  1074.             ys[3] = (0xfc & (l>>6));
  1075.             u = (char)l;
  1076.             v = *data++;
  1077.             u<<=2;
  1078.             v<<=2;
  1079.             y = ys[0];
  1080.             WRITEPIXEL_T;
  1081.             y = ys[1];
  1082.             WRITEPIXEL_T;
  1083.             lp += rowLongs - 2;
  1084.             y = ys[2];
  1085.             WRITEPIXEL_T;
  1086.             y = ys[3];
  1087.             WRITEPIXEL_T;
  1088.             lp -= rowLongs;
  1089.         }
  1090.     } else {
  1091.         while ( blen-- > 0 ) {
  1092.             l = *(long *)data;
  1093.             data += sizeof(long);
  1094.             ys[0] = (0xfc & (l>>24));
  1095.             ys[1] = (0xfc & (l>>18));
  1096.             ys[2] = (0xfc & (l>>12));
  1097.             ys[3] = (0xfc & (l>>6));
  1098.             u = (char)l;
  1099.             v = *data++;
  1100.             u<<=2;
  1101.             v<<=2;
  1102.             y = ys[0];
  1103.             WRITEPIXEL;
  1104.             y = ys[1];
  1105.             WRITEPIXEL;
  1106.             lp += rowLongs - 2;
  1107.             y = ys[2];
  1108.             WRITEPIXEL;
  1109.             y = ys[3];
  1110.             WRITEPIXEL;
  1111.             lp -= rowLongs;
  1112.         }
  1113.     }
  1114. }
  1115.  
  1116.  
  1117. #endif
  1118.  
  1119.  
  1120.